﻿//-----------------------------------------------------------------------
// <copyright file="BusyControl.cs" company="Noboru Project">
//      Copyright (c) Noboru Project. All rights reserved.
//
//      This source code is subject to terms and conditions of the New BSD License (BSD).
//      A copy of the license can be found in the License.html file at the root of this 
//      distribution. If you can not locate the New BSD License (BSD), please send an 
//      email to noboru_adm@live.com.
//      By using this source code in any fashion, you are agreeing to be bound by
//      the terms of the New BSD License (BSD) for Noboru.
//
//      You must not remove this notice, or any other, from this software.
// </copyright>
//-----------------------------------------------------------------------

using System;
using System.Net;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Documents;
using System.Windows.Ink;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Animation;
using System.Windows.Shapes;
using System.Windows.Controls.Primitives;
using System.ComponentModel;

namespace Noboru.Windows.Controls
{
    [TemplatePart(Name = "BackgroundPart", Type = typeof(Canvas))]
    [TemplatePart(Name = "DialogPart", Type = typeof(FrameworkElement))]
    public class BusyControl : ContentControl
    {
        public static readonly DependencyProperty OpenPositionProperty = DependencyProperty.Register("OpenPosition", typeof(DialogOpenPosition), typeof(BusyControl), new PropertyMetadata(DialogOpenPosition.Center));

        private Canvas _backgroundPart;
        private FrameworkElement _dialogPart;
        private Popup _popup;

        public BusyControl()
        {
            this.DefaultStyleKey = typeof(BusyControl);
        }

        public event EventHandler Closed;

        public event EventHandler<CancelEventArgs> Closing;

        public DialogOpenPosition OpenPosition
        {
            get
            {
                return (DialogOpenPosition)this.GetValue(OpenPositionProperty);
            }

            set
            {
                this.SetValue(OpenPositionProperty, value);
            }
        }

        private FrameworkElement DialogPart
        {
            get
            {
                return this._dialogPart;
            }

            set
            {
                this._dialogPart = value;
            }
        }

        public void Close()
        {
            CancelEventArgs cancelEventArgs = new CancelEventArgs();
            this.OnClosing(cancelEventArgs);
        }

        public void Show()
        {
            this.InitializeComponent();

            this._popup.Child = this;
            this._popup.IsOpen = true;
        }

        public override void OnApplyTemplate()
        {
            base.OnApplyTemplate();

            this._backgroundPart = (Canvas)GetTemplateChild("BackgroundPart");
            this.DialogPart = (FrameworkElement)GetTemplateChild("DialogPart");

            this.DetermineLayout();
            this.GoToState(false);
        
            Application.Current.Host.Content.Resized += new EventHandler(this.ParentContentResized);
        }

        protected virtual void OnClosed(EventArgs e)
        {
            if (this.Closed != null)
            {
                this.Closed(this, e);
            }

            if (this._popup != null)
            {
                if (this._popup.IsOpen)
                {
                    this._popup.IsOpen = false;
                }
            }
        }

        protected virtual void OnClosing(CancelEventArgs e)
        {
            if (this.Closing != null)
            {
                this.Closing(this, e);
            }

            if (e != null)
            {
                if (!e.Cancel)
                {
                    this.OnClosed(EventArgs.Empty);
                }
            }
        }

        private static void CenterControl(FrameworkElement element, Canvas parent)
        {
            if (element.ActualHeight == 0 || double.IsNaN(element.ActualHeight))
            {
                if (parent.ActualWidth > 0 && parent.ActualHeight > 0)
                {
                    element.Measure(new Size(parent.ActualWidth, parent.ActualHeight));
                }
                else
                {
                    element.Measure(new Size(parent.Width, parent.Height));
                }
            }

            if (parent.ActualWidth > 0 && parent.ActualHeight > 0)
            {
                element.SetValue(Canvas.TopProperty, Math.Ceiling((parent.ActualHeight / 2) - (element.DesiredSize.Height / 2)));
                element.SetValue(Canvas.LeftProperty, Math.Ceiling((parent.ActualWidth / 2) - (element.DesiredSize.Width / 2)));
            }
            else
            {
                element.SetValue(Canvas.TopProperty, Math.Ceiling((parent.Height / 2) - (element.DesiredSize.Height / 2)));
                element.SetValue(Canvas.LeftProperty, Math.Ceiling((parent.Width / 2) - (element.DesiredSize.Width / 2)));
            }
        }

        private static void CenterBottomControl(FrameworkElement element, Canvas parent)
        {
            if (element.ActualHeight == 0 || double.IsNaN(element.ActualHeight))
            {
                if (parent.ActualWidth > 0 && parent.ActualHeight > 0)
                {
                    element.Measure(new Size(parent.ActualWidth, parent.ActualHeight));
                }
                else
                {
                    element.Measure(new Size(parent.Width, parent.Height));
                }
            }

            if (parent.ActualWidth > 0 && parent.ActualHeight > 0)
            {
                element.SetValue(Canvas.TopProperty, Math.Ceiling(parent.ActualHeight - element.DesiredSize.Height));
                element.SetValue(Canvas.LeftProperty, Math.Ceiling((parent.ActualWidth / 2) - (element.DesiredSize.Width / 2)));
            }
            else
            {
                element.SetValue(Canvas.TopProperty, Math.Ceiling(parent.Height - element.DesiredSize.Height));
                element.SetValue(Canvas.LeftProperty, Math.Ceiling((parent.Width / 2) - (element.DesiredSize.Width / 2)));
            }
        }

        private static void CenterTopControl(FrameworkElement element, Canvas parent)
        {
            if (element.ActualHeight == 0 || double.IsNaN(element.ActualHeight))
            {
                if (parent.ActualWidth > 0 && parent.ActualHeight > 0)
                {
                    element.Measure(new Size(parent.ActualWidth, parent.ActualHeight));
                }
                else
                {
                    element.Measure(new Size(parent.Width, parent.Height));
                }
            }

            if (parent.ActualWidth > 0 && parent.ActualHeight > 0)
            {
                element.SetValue(Canvas.LeftProperty, Math.Ceiling((parent.ActualWidth / 2) - (element.DesiredSize.Width / 2)));
            }
            else
            {
                element.SetValue(Canvas.LeftProperty, Math.Ceiling((parent.Width / 2) - (element.DesiredSize.Width / 2)));
            }
        }

        private static void LeftTopControl(FrameworkElement element, Canvas parent)
        {
        }

        private static void LeftBottomControl(FrameworkElement element, Canvas parent)
        {
            if (element.ActualHeight == 0 || double.IsNaN(element.ActualHeight))
            {
                if (parent.ActualWidth > 0 && parent.ActualHeight > 0)
                {
                    element.Measure(new Size(parent.ActualWidth, parent.ActualHeight));
                }
                else
                {
                    element.Measure(new Size(parent.Width, parent.Height));
                }
            }

            if (parent.ActualWidth > 0 && parent.ActualHeight > 0)
            {
                element.SetValue(Canvas.TopProperty, Math.Ceiling(parent.ActualHeight - element.DesiredSize.Height));
            }
            else
            {
                element.SetValue(Canvas.TopProperty, Math.Ceiling(parent.Height - element.DesiredSize.Height));
            }
        }

        private static void LeftCenterControl(FrameworkElement element, Canvas parent)
        {
            if (element.ActualHeight == 0 || double.IsNaN(element.ActualHeight))
            {
                if (parent.ActualWidth > 0 && parent.ActualHeight > 0)
                {
                    element.Measure(new Size(parent.ActualWidth, parent.ActualHeight));
                }
                else
                {
                    element.Measure(new Size(parent.Width, parent.Height));
                }
            }

            if (parent.ActualWidth > 0 && parent.ActualHeight > 0)
            {
                element.SetValue(Canvas.TopProperty, Math.Ceiling((parent.ActualHeight / 2) - (element.DesiredSize.Height / 2)));
            }
            else
            {
                element.SetValue(Canvas.TopProperty, Math.Ceiling((parent.Height / 2) - (element.DesiredSize.Height / 2)));
            }
        }

        private void ParentContentResized(object sender, EventArgs e)
        {
            this.DetermineLayout();
        }

        private static void RightTopControl(FrameworkElement element, Canvas parent)
        {
            if (element.ActualHeight == 0 || double.IsNaN(element.ActualHeight))
            {
                if (parent.ActualWidth > 0 && parent.ActualHeight > 0)
                {
                    element.Measure(new Size(parent.ActualWidth, parent.ActualHeight));
                }
                else
                {
                    element.Measure(new Size(parent.Width, parent.Height));
                }
            }

            if (parent.ActualWidth > 0 && parent.ActualHeight > 0)
            {
                element.SetValue(Canvas.LeftProperty, Math.Ceiling(parent.ActualWidth - element.DesiredSize.Width));
            }
            else
            {
                element.SetValue(Canvas.LeftProperty, Math.Ceiling(parent.Width - element.DesiredSize.Width));
            }
        }

        private static void RightCenterControl(FrameworkElement element, Canvas parent)
        {
            if (element.ActualHeight == 0 || double.IsNaN(element.ActualHeight))
            {
                if (parent.ActualWidth > 0 && parent.ActualHeight > 0)
                {
                    element.Measure(new Size(parent.ActualWidth, parent.ActualHeight));
                }
                else
                {
                    element.Measure(new Size(parent.Width, parent.Height));
                }
            }

            if (parent.ActualWidth > 0 && parent.ActualHeight > 0)
            {
                element.SetValue(Canvas.TopProperty, Math.Ceiling((parent.ActualHeight / 2) - (element.DesiredSize.Height / 2)));
                element.SetValue(Canvas.LeftProperty, Math.Ceiling(parent.ActualWidth - (element.DesiredSize.Width / 2)));
            }
            else
            {
                element.SetValue(Canvas.TopProperty, Math.Ceiling((parent.Height / 2) - (element.DesiredSize.Height / 2)));
                element.SetValue(Canvas.LeftProperty, Math.Ceiling(parent.Width - element.DesiredSize.Width));
            }
        }

        private static void RightBottomControl(FrameworkElement element, Canvas parent)
        {
            if (element.ActualHeight == 0 || double.IsNaN(element.ActualHeight))
            {
                if (parent.ActualWidth > 0 && parent.ActualHeight > 0)
                {
                    element.Measure(new Size(parent.ActualWidth, parent.ActualHeight));
                }
                else
                {
                    element.Measure(new Size(parent.Width, parent.Height));
                }
            }

            if (parent.ActualWidth > 0 && parent.ActualHeight > 0)
            {
                element.SetValue(Canvas.TopProperty, Math.Ceiling(parent.ActualHeight - element.DesiredSize.Height));
                element.SetValue(Canvas.LeftProperty, Math.Ceiling(parent.ActualWidth - element.DesiredSize.Width));
            }
            else
            {
                element.SetValue(Canvas.TopProperty, Math.Ceiling(parent.Height - element.DesiredSize.Height));
                element.SetValue(Canvas.LeftProperty, Math.Ceiling(parent.Width - element.DesiredSize.Width));
            }
        }

        private void GoToState(bool useTransitions)
        {
        }

        private void InitializeComponent()
        {
            if (this._popup != null)
            {
                return;
            }

            this._popup = new Popup();
        }

        private void DetermineLayout()
        {
            if (this._backgroundPart != null)
            {
                this._backgroundPart.Width = Application.Current.Host.Content.ActualWidth;
                this._backgroundPart.Height = Application.Current.Host.Content.ActualHeight;

                this.PositionWindow();
            }
        }

        private void PositionWindow()
        {
            if (!DesignerProperties.GetIsInDesignMode(this._dialogPart))
            {
                switch (this.OpenPosition)
                {
                    case DialogOpenPosition.TopLeft:
                        LeftTopControl(this._dialogPart, this._backgroundPart);
                        break;

                    case DialogOpenPosition.TopCenter:
                        CenterTopControl(this._dialogPart, this._backgroundPart);
                        break;

                    case DialogOpenPosition.TopRight:
                        RightTopControl(this._dialogPart, this._backgroundPart);
                        break;

                    case DialogOpenPosition.CenterLeft:
                        LeftCenterControl(this._dialogPart, this._backgroundPart);
                        break;

                    case DialogOpenPosition.Center:
                        CenterControl(this._dialogPart, this._backgroundPart);
                        break;

                    case DialogOpenPosition.CenterRight:
                        RightCenterControl(this._dialogPart, this._backgroundPart);
                        break;

                    case DialogOpenPosition.BottomLeft:
                        LeftBottomControl(this._dialogPart, this._backgroundPart);
                        break;

                    case DialogOpenPosition.BottomCenter:
                        CenterBottomControl(this._dialogPart, this._backgroundPart);
                        break;

                    case DialogOpenPosition.BottomRight:
                        RightBottomControl(this._dialogPart, this._backgroundPart);
                        break;

                    default:
                        CenterControl(this._dialogPart, this._backgroundPart);
                        break;
                }
            }
        }
    }
}
